/* ======================================================================== */
/*  DMA Stream abstractions for Imaging.                                    */
/*                                                                          */
/*  These streams tie a large "external" buffer with a small "internal"     */
/*  buffer, and provide a generalized interface for streaming data to/from  */
/*  the external buffer from/to the internal buffer.                        */
/* ======================================================================== */
#define MYDAT_2D1D   0
#define MYDAT_1D2D   1

#include "dstr_2d.h"   
#include "csl_dat.h"
#include "csl_irq.h"

/*#define DSTR_DEBUG*/

#ifdef DSTR_DEBUG
# include <stdio.h>
# define dprintf(x) printf x ; fflush(stdout)
#else
# define dprintf(x) 
#endif

/* ======================================================================== */
/*  DSTR_OPEN    -- Initialize a new DMA Stream object.                     */
/* ======================================================================== */

int dstr_open
(
    dstr_t         *dstr,       /* DMA Stream structure.                    */
    void           *x_data,     /* "External" data buffer.                  */
    int             x_size,     /* Size of external data buffer.            */
    void           *i_data,     /* "Internal" data buffer.                  */
    unsigned short  i_size,     /* Size of internal data buffer.            */
    unsigned short  quantum,    /* Size of a single transfer (get or put)   */
    unsigned short  multiple,   /* Number of lines to fetch                 */
    unsigned short  stride,     /* Stride amount to increment ext. memory   */
    unsigned short  w_size,     /* Window size.  Set to 1 for double buf.   */     
    dstr_dir_t      dir         /* Direction (Input or Output)              */
)
{
  
    
    /* -------------------------------------------------------------------- */
    /*  Sanity check all of the arguments.   We might consider providing    */
    /*  a range of error codes here, rather than just -1, but for now,      */
    /*  this will do.                                                       */
    /* -------------------------------------------------------------------- */
    if (!dstr || !x_data || !i_data || !quantum || !i_size || !multiple)     
                                                                return -1;
    if (dir == DSTR_OUTPUT && w_size != 1)                      return -2;
    if ((2 * w_size * quantum * multiple) > i_size || w_size < 1)
    {
      dprintf(("i_size:%d, exp_size:%d \n",
              i_size, (2 * w_size * quantum * multiple)));            
      return -3;
    }

    /* -------------------------------------------------------------------- */
    /*  Poke all the appropriate values into the structure.                 */
    /*  Make sure our external and internal sizes are rounded to multiples  */
    /*  of our transfer quantum.                                            */
    /* -------------------------------------------------------------------- */
    dstr->x_data   = (char *) x_data;
    dstr->x_size   = x_size - (x_size % (stride * multiple));
    
    dstr->i_data   = (char *) i_data;
    dstr->i_size   = (2 * quantum * multiple * w_size); 
    
    dstr->i_ofs    = 0;
    dstr->quantum  = quantum;
    dstr->multiple = multiple;  
    
    dstr->w_size   = (w_size * multiple *quantum); 
    dstr->stride   = stride;
    dstr->x_ofs    = 0; 
       
  
    /* -------------------------------------------------------------------- */
    /*  If this is an external-to-internal transfer, start off the initial  */
    /*  transfers to pipe up the stream.                                    */
    /* -------------------------------------------------------------------- */
    if (dir == DSTR_INPUT)
    {
        
        unsigned id; 
                        
        dprintf(("PREFETCH: ACTIVE = %d\n", dstr->x_ofs)); 
        
        id = DAT_copy2d( DAT_2D1D,
                         (void*) (dstr->x_data), 
                         (void*) (dstr->i_data),
                         dstr->quantum,
                         w_size * multiple,
                         dstr->stride);

        dstr->x_ofs   += (dstr->stride  * multiple * w_size);
        dstr->i_ofs   += (dstr->quantum * multiple * w_size);
        dstr->xfer_id = id;
    }
    else
    /* -------------------------------------------------------------------- */
    /*  If this is an internal-to-external transfer, start our x_ofs at -1  */
    /*  to indicate that this stream has not yet been written to.           */
    /* -------------------------------------------------------------------- */
    {
        dstr->x_ofs = -1;
    }

    return 0;
} 

/* ======================================================================== */
/*  DSTR_GET     -- Get the next buffer from a stream.                      */
/* ======================================================================== */
void * dstr_get(dstr_t *dstr)
{
    unsigned    id;
    char        *ready, *active;
    int         do_copy = 0;

    /* -------------------------------------------------------------------- */
    /*  Remember the pointer to the currently ready buffer, and generate    */
    /*  a pointer to the new active buffer.                                 */
    /*                                                                      */
    /*  The ready buffer is always 'w_size' quanta behind the active        */
    /*  buffer, modulo the total buffer size.  The conditional handles      */
    /*  the one wraparound case.  The input offset marches from 'w_size -   */
    /*  quantum' thru 'i_size - quantum'.                                   */
    /*                                                                      */
    /*  We also decide whether an extra 'copy' transfer is issued here,     */
    /*  based on the current value of i_ofs relative to the window size.    */
    /* -------------------------------------------------------------------- */
    active  = dstr->i_data + dstr->i_ofs;
    do_copy = dstr->i_ofs >= (dstr->w_size + dstr->quantum);

    if (dstr->i_ofs >= dstr->w_size)
        ready = dstr->i_data + dstr->i_ofs - dstr->w_size;  /* Normal       */
    else
        ready = dstr->i_data + dstr->w_size;                /* Wraparound   */

    /* -------------------------------------------------------------------- */
    /*  Update our input offset for the next transfer, and handle its       */
    /*  wraparound case as well.                                            */
    /* -------------------------------------------------------------------- */
    dstr->i_ofs  += dstr->quantum;
    if (dstr->i_ofs >= dstr->i_size)
        dstr->i_ofs = dstr->w_size - dstr->quantum;

    dprintf(("ACTIVE: %3d  READY: %3d  COPY: %d  W_SIZE: %d\n", 
             active - i_data, ready - i_data, do_copy, w_size));

    /* -------------------------------------------------------------------- */
    /*  Fire off a new transfer for the next buffer.                        */
    /* -------------------------------------------------------------------- */
    if (dstr->x_ofs < dstr->x_size)
    {   
        
        id = DAT_copy((void *) (dstr->x_data + dstr->x_ofs), 
                      (void *) (active),
                      dstr->quantum);
    }

    /* -------------------------------------------------------------------- */
    /*  Handle extra copy for sliding window.   We replace the previous     */
    /*  "ready" line with a copy of the new "active" line.   In the case    */
    /*  where we do this extra copy, we block on the extra copy only.       */
    /* -------------------------------------------------------------------- */
    if (do_copy)
    { 
        
        id = DAT_copy((void *) (active), 
                      (void *) (ready - dstr->quantum), 
                      dstr->quantum);
    }

    /* -------------------------------------------------------------------- */
    /*  Actually wait the ready buffer, to be sure it's truly ready.        */
    /* -------------------------------------------------------------------- */
    DAT_wait(dstr->xfer_id);

    /* -------------------------------------------------------------------- */
    /*  Remember the new transfer ID, and update our external data pointer. */
    /* -------------------------------------------------------------------- */
    dstr->xfer_id = id;
    dstr->x_ofs  += dstr->stride;

    /* -------------------------------------------------------------------- */
    /*  Return the pointer to the ready buffer.  We're done.                */
    /* -------------------------------------------------------------------- */
    return (void*) ready;
}

/* ======================================================================== */
/*  DSTR_PUT     -- Write out the next buffer in a double-buffer.           */
/* ======================================================================== */
void *dstr_put(dstr_t *dstr)
{
    unsigned id;
    char *ready;
    
    /* -------------------------------------------------------------------- */
    /*  If x_ofs < 0, then we haven't started yet.  Return the initial      */
    /*  buffer pointer for the user to start writing data.                  */
    /* -------------------------------------------------------------------- */
    if (dstr->x_ofs < 0)
    {
        dstr->x_ofs = 0;
        dstr->i_ofs = 0;        
        
        id = DAT_copy((void *) (dstr->i_data + dstr->i_ofs),
                      (void *) (dstr->x_data + dstr->x_ofs),
                      1); 
        
        dstr->xfer_id = id;
        return (void *) dstr->i_data;
    }

    /* -------------------------------------------------------------------- */
    /*  Otherwise, fire off the copy for the dirty buffer.                  */
    /* -------------------------------------------------------------------- */
    if (dstr->x_ofs < dstr->x_size)
    {   
                
        id = DAT_copy((void *) (dstr->i_data + dstr->i_ofs),
                      (void *) (dstr->x_data + dstr->x_ofs),
                      dstr->quantum); 
    }

    /* -------------------------------------------------------------------- */
    /*  Generate the pointer to the newly ready buffer, and move our        */
    /*  output's data pointer.                                              */
    /* -------------------------------------------------------------------- */
    dstr->x_ofs += dstr->stride;
    dstr->i_ofs ^= dstr->quantum;
    ready        = dstr->i_data + dstr->i_ofs;

    /* -------------------------------------------------------------------- */
    /*  Wait for the "ready" buffer to truly be ready.                      */
    /* -------------------------------------------------------------------- */
    DAT_wait(dstr->xfer_id);

    /* -------------------------------------------------------------------- */
    /*  Remember our dirty buffer's transfer ID.                            */
    /* -------------------------------------------------------------------- */
    dstr->xfer_id = id;

    /* -------------------------------------------------------------------- */
    /*  Return the pointer to the readied output buffer.                    */
    /* -------------------------------------------------------------------- */
    return (void *) ready;
} 


/* ======================================================================== */
/*  DSTR_GET     -- Get the next buffer from a stream.                      */
/* ======================================================================== */
void *dstr_get_2D(dstr_t *dstr)
{
    unsigned    id;
    char        *ready, *active;
    int         do_copy = 0;

    /* -------------------------------------------------------------------- */
    /*  Remember the pointer to the currently ready buffer, and generate    */
    /*  a pointer to the new active buffer.                                 */
    /*                                                                      */
    /*  The ready buffer is always 'w_size' quanta behind the active        */
    /*  buffer, modulo the total buffer size.  The conditional handles      */
    /*  the one wraparound case.  The input offset marches from 'w_size -   */
    /*  quantum' thru 'i_size - quantum'.                                   */
    /*                                                                      */
    /*  We also decide whether an extra 'copy' transfer is issued here,     */
    /*  based on the current value of i_ofs relative to the window size.    */
    /* -------------------------------------------------------------------- */
    active  = dstr->i_data + dstr->i_ofs;
    do_copy = dstr->i_ofs >= (dstr->w_size + (dstr->quantum * dstr->multiple));

    if (dstr->i_ofs >= dstr->w_size)
        ready = dstr->i_data + dstr->i_ofs - dstr->w_size;  /* Normal       */
    else
        ready = dstr->i_data + dstr->w_size;                /* Wraparound   */

    /* -------------------------------------------------------------------- */
    /*  Update our input offset for the next transfer, and handle its       */
    /*  wraparound case as well.                                            */
    /* -------------------------------------------------------------------- */
    dstr->i_ofs  += (dstr->quantum * dstr->multiple);
    if (dstr->i_ofs >= dstr->i_size)
        dstr->i_ofs = dstr->w_size - (dstr->quantum * dstr->multiple);

    dprintf(("ACTIVE: %3d  READY: %3d  COPY: %d  W_SIZE: %d\n", 
             active - i_data, ready - i_data, do_copy, w_size));

    /* -------------------------------------------------------------------- */
    /*  Fire off a new transfer for the next buffer.                        */
    /* -------------------------------------------------------------------- */
    if (dstr->x_ofs < dstr->x_size)
    {
        id = DAT_copy2d( DAT_2D1D,
                         (void *) (dstr->x_data + dstr->x_ofs), 
                         (void *) (active),
                         dstr->quantum,
                         dstr->multiple,
                         dstr->stride);
    }

    /* -------------------------------------------------------------------- */
    /*  Handle extra copy for sliding window.   We replace the previous     */
    /*  "ready" line with a copy of the new "active" line.   In the case    */
    /*  where we do this extra copy, we block on the extra copy only.       */
    /* -------------------------------------------------------------------- */
    if (do_copy)
    {
        id = DAT_copy((void *) (active), 
                      (void *) (ready - (dstr->quantum * dstr->multiple)), 
                      dstr->quantum * dstr->multiple);
    }

    /* -------------------------------------------------------------------- */
    /*  Actually wait the ready buffer, to be sure it's truly ready.        */
    /* -------------------------------------------------------------------- */
    DAT_wait(dstr->xfer_id);

    /* -------------------------------------------------------------------- */
    /*  Remember the new transfer ID, and update our external data pointer. */
    /* -------------------------------------------------------------------- */
    dstr->xfer_id = id;
    dstr->x_ofs  += (dstr->stride * dstr->multiple);

    /* -------------------------------------------------------------------- */
    /*  Return the pointer to the ready buffer.  We're done.                */
    /* -------------------------------------------------------------------- */
    return (void*) ready;
}

/* ======================================================================== */
/*  DSTR_PUT     -- Write out the next buffer in a double-buffer.           */
/* ======================================================================== */
void *dstr_put_2D(dstr_t *dstr)
{
    unsigned id;
    char *ready;

    /* -------------------------------------------------------------------- */
    /*  If x_ofs < 0, then we haven't started yet.  Return the initial      */
    /*  buffer pointer for the user to start writing data.                  */
    /* -------------------------------------------------------------------- */
    if (dstr->x_ofs < 0)
    {
        dstr->x_ofs = 0;
        dstr->i_ofs = 0;
        id = DAT_copy((void *) (dstr->i_data + dstr->i_ofs),
                      (void *) (dstr->x_data + dstr->x_ofs),
                      1);
        dstr->xfer_id = id;
        return (void *) dstr->i_data;
    }

    /* -------------------------------------------------------------------- */
    /*  Otherwise, fire off the copy for the dirty buffer.                  */
    /* -------------------------------------------------------------------- */
    if (dstr->x_ofs < dstr->x_size)
    {
        id = DAT_copy2d(  DAT_1D2D,
                          (void *) (dstr->i_data + dstr->i_ofs),
                          (void *) (dstr->x_data + dstr->x_ofs),
                          dstr->quantum,
                          dstr->multiple,
                          dstr->stride);
    }

    /* -------------------------------------------------------------------- */
    /*  Generate the pointer to the newly ready buffer, and move our        */
    /*  output's data pointer.                                              */
    /* -------------------------------------------------------------------- */
    dstr->x_ofs += dstr->stride * dstr->multiple;
    dstr->i_ofs ^= (dstr->quantum * dstr->multiple);
    ready        = dstr->i_data + dstr->i_ofs;

    /* -------------------------------------------------------------------- */
    /*  Wait for the "ready" buffer to truly be ready.                      */
    /* -------------------------------------------------------------------- */
    DAT_wait(dstr->xfer_id);

    /* -------------------------------------------------------------------- */
    /*  Remember our dirty buffer's transfer ID.                            */
    /* -------------------------------------------------------------------- */
    dstr->xfer_id = id;

    /* -------------------------------------------------------------------- */
    /*  Return the pointer to the readied output buffer.                    */
    /* -------------------------------------------------------------------- */
    return (void *) ready;
} 

/* ======================================================================== */
/*  DSTR_REWUIND    -- Initialize a new DMA Stream object.                  */
/* ======================================================================== */
int dstr_rewind
(
    dstr_t         *dstr,       /* DMA Stream structure.                    */
    void           *x_data,     /* "External" data buffer.                  */
    dstr_dir_t      dir,        /* Direction (Input or Output)              */             
    unsigned short  w_size      /* Window size.  Set to 1 for double buf.   */  
)
{
  
     

    /* -------------------------------------------------------------------- */
    /*  Poke all the appropriate values into the structure.                 */
    /*  Make sure our external and internal sizes are rounded to multiples  */
    /*  of our transfer quantum.                                            */
    /* -------------------------------------------------------------------- */
    dstr->x_data   = (char *) x_data;   
    dstr->i_ofs    = 0;
    dstr->x_ofs    = 0;
    
     
    
    if (dir == DSTR_OUTPUT)
    {
        dstr->x_ofs = -1;
        DAT_wait(dstr->xfer_id);
    }   
  
    /* -------------------------------------------------------------------- */
    /*  If this is an external-to-internal transfer, start off the initial  */
    /*  transfers to pipe up the stream.                                    */
    /* -------------------------------------------------------------------- */
    if (dir == DSTR_INPUT)
    {
        
        unsigned id; 
                        
        dprintf(("PREFETCH: ACTIVE = %d\n", dstr->x_ofs));

        id = DAT_copy2d( DAT_2D1D,
                         (void*) (dstr->x_data), 
                         (void*) (dstr->i_data),
                         dstr->quantum,
                         w_size * dstr->multiple,
                         dstr->stride);

        dstr->x_ofs   += (dstr->stride  * dstr->multiple * w_size);
        dstr->i_ofs   += (dstr->quantum * dstr->multiple * w_size);
        dstr->xfer_id = id;
    }    
    
    return 0;
} 



/*--------------------------------------------------------------------------*/
/* Function obtains transfer id and makes sure that DAT_wait completes.     */
/*--------------------------------------------------------------------------*/

void dstr_close(dstr_t *dstr)
{
    unsigned id;
    
    id = dstr->xfer_id;
    
    DAT_wait(id);
}

/* ======================================================================== */
/*  End of file:  dstr.c                                                    */
/* ------------------------------------------------------------------------ */
/*            Copyright (c) 2000 Texas Instruments, Incorporated.           */
/*                           All Rights Reserved.                           */
/* ======================================================================== */
